package com.qianji.petmall.order.config;

import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;

/**
 * @author liang zai
 * @date 2022/4/21 14:47
 * @description
 */
@Configuration
public class MyRabbitConfig {

    @Autowired
    private RabbitTemplate rabbitTemplate;
//    @Bean
//    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
//        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
//        initRabbitTemplate();
//        return rabbitTemplate;
//    }

    @Bean
    public MessageConverter messageConverter(){
        Jackson2JsonMessageConverter jackson2JsonMessageConverter = new Jackson2JsonMessageConverter();
        return jackson2JsonMessageConverter;
    }

    /**
     * 定制RabbitTemplate
     * 1、服务收到消息就会回调
     *      1、spring.rabbitmq.publisher-confirms: true
     *      2、设置确认回调
     * 2、消息正确抵达队列就会进行回调
     *      1、spring.rabbitmq.publisher-returns: true
     *         spring.rabbitmq.template.mandatory: true
     *      2、设置确认回调ReturnCallback
     *
     * 3、消费端确认(保证每个消息都被正确消费，此时才可以broker删除这个消息)
     *
     */
    @PostConstruct //MyRabbitConfig 对象构造器创建完以后执行这个方法
    public void initRabbitTemplate(){
        //确认消息是否到达Broker代理
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            /**
             * 1、只要消息抵达Broker就ack=true
             * correlationData：当前消息的唯一关联数据(这个是消息的唯一id)
             * ack：消息是否成功收到
             * cause：失败的原因
             * 消费者获取到消息，成功处理，可以回复Ack给Broker
             * basic.ack用于肯定确认；broker将移除此消息
             * basic.nack用于否定确认；可以指定broker是否丢弃此消息，可以批量
             * basic.reject用于否定确认；同上，但不能批量
             * 默认自动ack，消息被消费者收到，就会从broker的queue中移除
             * queue无消费者，消息依然会被存储，直到消费者消费
             * 消费者收到消息，默认会自动ack。但是如果无法确定此消息是否被处理完成，或者成功处理。我们可以开启手动ack模式
             * 消息处理成功，ack()，接受下一个消息，此消息broker就会移除
             * 消息处理失败，nack()/reject()，重新发送给其他人进行处理，或者容错处理后ack
             * 消息一直没有调用ack/nack方法，broker认为此消息正在被处理，不会投递给别人，此时客户端断开，消息不会被broker移除，会投递给别人
             */
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                System.out.println("confirm....correlationData:"+correlationData+"confirm....ack:"+ack+"confirm....cause:"+cause);
            }
        });
        //开启消息是否抵达队列的确认
        rabbitTemplate.setReturnsCallback((returnedMessage)->{
            /**
             * 只要消息没有投递给指定的队列，就触发这个失败回调
             * message：投递失败的消息详细信息
             * replyCode：回复的状态码
             * replyText：回复的文本内容
             * exchange：当时这个消息发给哪个交换机
             * routingKey：当时这个消息用哪个路邮键
             */
            System.out.println("Fail Message["+returnedMessage.getMessage()+"]==>replyCode["+returnedMessage.getReplyCode()+
                    "]==>replyText["+returnedMessage.getReplyText()+"]==>exchange" +
                    "["+returnedMessage.getExchange()+"]==>routingKey["+returnedMessage.getRoutingKey()+"]");
        });
    }
}
